package common

import (
	"compress/gzip"
	"encoding/xml"
	"fmt"
	"github.com/imroc/req"
	"github.com/thedevsaddam/retry"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"
	"unsafe"
)

// func parse(target interface{}, body io.Reader) error {
// 	xmlDecoder := xml.NewDecoder(body)
// 	err := xmlDecoder.Decode(target)
//
// 	if err != nil {
// 		return err
// 	}
//
// 	return nil
// }

// func ParseMetadata(body io.Reader) (*Metadata, error) {
// 	metadata := &Metadata{}
//
// 	err := parse(metadata, body)
// 	if err != nil {
// 		return nil, err
// 	}
// 	return metadata, nil
// }

// func ParseRepomd(body io.Reader) (*Repomd, error) {
// 	repomd := &Repomd{}
//
// 	err := parse(repomd, body)
// 	if err != nil {
// 		return nil, err
// 	}
// 	return repomd, nil
// }

func getMemoryPackages(s []Package) int {
	size := 0
	s = s[:cap(s)]
	size += cap(s) * int(unsafe.Sizeof(s))
	for i := range s {
		size += (&s[i]).size()
	}
	return size
}

func readXml(data io.Reader, metadata *Metadata, feedCall func(p Package)) error {
	reader, err := gzip.NewReader(data)

	if err != nil {
		return err
	}

	defer reader.Close()

	if reader != nil {
		bytes, err := ioutil.ReadAll(reader)

		if err != nil {
			return err
		}

		err = xml.Unmarshal(bytes, &metadata)

		if err != nil {
			return err
		}

		// 排序
		packages := Packages{}

		for _, v1 := range metadata.Packages {
			if v1.Arch == "x86_64" || v1.Arch == "noarch" {
				packages = append(packages, v1)
			}
		}

		packages.Sort()

		// fmt.Println(unsafe.Sizeof(packages))
		// fmt.Println(getMemoryPackages(packages))

		for _, v2 := range packages {
			// fmt.Println(fmt.Sprintf("%s %s %s", v2.Name, v2.Arch, v2.Version))
			feedCall(v2)
		}

		// for _, v2 := range metadata.Packages {
		// 	if v2.Arch == "x86_64" || v2.Arch == "noarch" {
		// 		feedCall(v2)
		// 	}
		// }
	}

	return nil
}

func getUrlResp(url string) (*req.Resp, error) {
	resp, err := req.Get(url)

	if err != nil {
		return nil, err
	} else {
		if resp.Response().StatusCode != 200 {
			if resp.Response().StatusCode == 404 {
				return nil, nil
			} else {
				return nil, fmt.Errorf("HTTP Status Code = %d. (%s)", resp.Response().StatusCode, resp.Request().URL)
			}
		} else {
			return resp, nil
		}
	}
}

// PackagesIter 获取 Yum 仓库软件清单
func PackagesIter(repoBaseUrl string, proxyUrl string, feedCall func(p Package)) error {
	err := req.SetProxy(func(r *http.Request) (*url.URL, error) {
		if proxyUrl != "" {
			return url.Parse(proxyUrl)
		}
		return nil, nil
	})

	resp, err := retry.Do(3, 1*time.Second, getUrlResp, fmt.Sprintf("%s/repodata/repomd.xml", repoBaseUrl))

	if err != nil {
		return err
	}

	if resp[0] == nil {
		return nil
	}

	repomdXmlResult := Repomd{}

	err = resp[0].(*req.Resp).ToXML(&repomdXmlResult)

	if err != nil {
		return err
	}

	metadata := Metadata{}

	for _, v1 := range repomdXmlResult.Data {
		if v1.Type == "primary" {
			primaryXmlUrl := fmt.Sprintf("%s/%s", repoBaseUrl, v1.Location.Href)

			resp, err := req.Get(primaryXmlUrl)

			if err != nil {
				return err
			}

			err = readXml(resp.Response().Body, &metadata, feedCall)

			if err != nil {
				return err
			}

			break
		}
	}

	return nil
}
